﻿#include "local_command.hh"
#include "../../thirdparty/jsoncpp/include/json/json.h"
#include "../detail/service_layer.hh"
#include "../box/fixed_mem_pool.hh"
#include "../box/proc_stat.hh"
#include "../box/service_context.hh"
#include "../detail/box_argument_impl.hh"
#include "../detail/box_config_impl.hh"
#include "../detail/command_impl.hh"
#include "../detail/command_manager.hh"
#include "../detail/http_base_impl.hh"
#include "../detail/memory_allocator_impl.hh"
#include "../util/string_util.hh"
#include "../util/time_util.hh"
#include "service_box.hh"
#include <chrono>
#include <fstream>
#include <iostream>
#include <thread>

kratos::service::LocalCommand::LocalCommand(ServiceBox *box) {
  box_ = box;
}

kratos::service::LocalCommand::~LocalCommand() {
  stop();
}

auto kratos::service::LocalCommand::start() -> bool {
  register_all_command();
  return true;
}

auto kratos::service::LocalCommand::stop() -> void {
  stop_command_.reset();
  reload_command_.reset();
  proc_stat_command_.reset();
}

auto kratos::service::LocalCommand::do_command(const std::string &command)
    -> void {
  if (!box_->get_argument().get_host().empty()) {
    std::vector<std::string> result;
    util::split(box_->get_argument().get_host(), ":", result);
    int port = 6889;
    try {
      if (result.size() == 2) {
        port = std::stoi(result[1]);
      }
    } catch (std::exception &e) {
      box_->write_log(
        lang::LangID::LANG_UNEXPECTED_EXCEPTION,
        klogger::Logger::FAILURE, "LocalCommand",
        util::demangle(typeid(e).name()).c_str(), e.what());
      return;
    }
    send_command(command, result[0], port);
  } else {
    send_command(command, DEFAULT_HOST, DEFAULT_PORT);
  }
}

auto kratos::service::LocalCommand::send_command(
  const std::string &command,
  const std::string &host,
  int port) -> void {
  http::HttpBaseImpl http(box_);
  if (!http.start()) {
    return;
  }
  Json::Value root;
  root["command"] = command;
  bool done = false;
  http.do_request_async(
    host,
    port,
    "/",
    "PUT",
    {},
    root.toStyledString(),
    DEFAULT_TIMEOUT / 1000,
    0,
    [&](http::HttpCallPtr response, std::uint64_t) {
      done = true;
      Json::Value root;
      Json::String error;
      auto content = response.lock()->get_content();
      const auto *start = content.c_str();
      const auto *end = content.c_str() + content.size();
      if (util::get_json_root(content, root, error)) {
        if (root["show-alloc"]) {
          auto content = root["show-alloc"].asString();
          print_json(content.c_str(), content.size());
        } else if (root["show-config"]) {
          auto content = root["show-config"].asString();
          print_json(content.c_str(), content.size());
        } else {
          std::cout << response.lock()->get_content() << std::endl;
        }
      }
    });
  auto start = util::get_os_time_millionsecond();
  while (!done) {
    http.update(util::get_os_time_millionsecond());
    if (util::get_os_time_millionsecond() - start > DEFAULT_TIMEOUT) {
      std::cout << "Execute command timeout" << std::endl;
      break;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  }
  http.stop();
}

auto kratos::service::LocalCommand::register_all_command() -> void {
  using namespace kratos::config;

  // 注册退出命令
  stop_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  stop_command_->wait_for(
    "stop",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      box_->set_wait_stop_flag();
      Json::Value root;
      root["error"] = "ok";
      return root.toStyledString();
    });
  // 注册重载配置命令
  reload_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  reload_command_->wait_for(
    "reload",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      std::string error;
      auto &config = dynamic_cast<BoxConfigImpl &>(box_->get_config());
      const auto &config_file_path = config.get_config_file_path();
      config.reload(config_file_path, error);
      Json::Value root;
      root["error"] = "ok";
      return root.toStyledString();
    });
  // 注册进程信息命令
  proc_stat_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  proc_stat_command_->wait_for(
    "proc_stat",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      auto *stat = box_->get_proc_stat();
      Json::Value root;
      root["error"] = "ok";
      ValueMap value_map;
      stat->get(value_map);
      Json::Value info_value;
      for (const auto &[k, v] : value_map) {
        info_value[k] = v;
      }
      root["proc_stat"] = info_value;
      return root.toStyledString();
    });
  // 注册分配信息命令
  show_alloc_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  show_alloc_command_->wait_for(
    "show-alloc",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      auto &allocator = dynamic_cast<MemoryAllocatorImpl &>(
        box_->get_context()->get_allocator());
      Json::Value root;
      root["error"] = "ok";
      std::stringstream ss;
      allocator.dump(ss);
      root["show-alloc"] = ss.str();
      return root.toStyledString();
    });
  // 注册获取已注册服务命令
  show_register_service_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  show_register_service_command_->wait_for(
    "show-register-service",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      Json::Value root;
      root["error"] = "ok";
      Json::Value services;
      for (const auto &[k, v] : box_->get_register_services()) {
        services[k] = v;
      }
      root["show-register-service"] = services;
      return root.toStyledString();
    });
  // 注册获取相关联的服务
  show_remote_service_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  show_remote_service_command_->wait_for(
    "show-remote-service",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      Json::Value root;
      root["error"] = "ok";
      Json::Value services;
      auto *service_layer = box_->get_service_layer();
      for (const auto& [k,_] : service_layer->get_remote_service()) {
        services.append(k);
      }
      root["show-remote-service"] = services;
      return root.toStyledString();
    });
  // 注册显示配置的命令
  show_config_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  show_remote_service_command_->wait_for(
    "show-config",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      Json::Value root;
      root["error"] = "ok";
      root["show-config"] = get_config();
      return root.toStyledString();
    });
  // 注册容器内存分配信息
  show_sys_alloc_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  show_remote_service_command_->wait_for(
    "show-sys-alloc",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      Json::Value root;
      root["error"] = "ok";
      Json::Value sys_mem_info;
      sys_mem_info["Total memory block in use"] = MempoolRef.count_in_use();
      sys_mem_info["Total memory block in use(bytes)"] =
        MempoolRef.mem_block_size_in_use();
      sys_mem_info["Total memory block in use(k)"] =
        MempoolRef.mem_block_size_in_use() / 1024;
      const auto &fixed_pools = MempoolRef.get_fixed_mem_pools();
      std::size_t total_block_count_in_pool = 0;
      std::size_t total_mem_size_in_pool = 0;
      Json::Value sys_mem_info_detail;
      for (const auto &[size, pool] : fixed_pools) {
        total_block_count_in_pool += pool->count_in_pool();
        total_mem_size_in_pool +=
          total_block_count_in_pool * pool->fixed_size();
        sys_mem_info_detail[std::to_string(pool->fixed_size())] =
          std::to_string(pool->count_in_pool()) + "," +
          std::to_string(pool->fixed_size() * pool->count_in_pool());
      }
      sys_mem_info["Total memory block in pool"] = total_block_count_in_pool;
      sys_mem_info["Total memory block in pool(bytes)"] =
        total_mem_size_in_pool;
      sys_mem_info["Total memory block in pool(k)"] =
        total_mem_size_in_pool / 1024;
      sys_mem_info["Trunk"] = sys_mem_info_detail;
      root["show-sys-alloc"] = sys_mem_info;
      return root.toStyledString();
    });
  // 注册强制GC的命令
  force_gc_command_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  force_gc_command_->wait_for(
    "force-gc",
    DEFAULT_TIMEOUT,
    [&](const std::string &) -> std::string {
      Json::Value root;
      root["error"] = "ok";
      MempoolRef.release_all();
      return root.toStyledString();
    });
  // 改变命令行设置
  change_argument_setting_ = make_unique_pool_ptr<CommandImpl>(
    box_->get_command_manager());
  change_argument_setting_->wait_for(
    "change-argument-setting",
    DEFAULT_TIMEOUT,
    [&](const std::string& command) -> std::string {
      Json::Value root;
      root["error"] = "ok";
      const auto& impl = dynamic_cast<const kratos::argument::BoxArgumentImpl&>(
        box_->get_argument());
      std::string result;
      try {
        if (!impl.on_change(command, result)) {
          root["error"] = "fail";
        }
        root["result"] = result;
      } catch (std::exception& e) {
        root["error"] = "fail";
        root["result"] = e.what();
      }
      return root.toStyledString();
    });
}

auto kratos::service::LocalCommand::get_config() -> std::string {
  auto &config =
    dynamic_cast<kratos::config::BoxConfigImpl &>(box_->get_config());
  const auto &config_file_path = config.get_config_file_path();
  std::fstream fs;
  fs.open(config_file_path, std::ios::in);
  if (!fs) {
    return "";
  }
  std::string content = "\n";
  std::string line;
  while (std::getline(fs, line)) {
    content += line + "\n";
  }
  fs.close();
  return content;
}

auto kratos::service::LocalCommand::print_json(const char *str,
  std::size_t count) -> void {
  std::size_t index = 0;
  char c = str[0];
  bool escape = false;
  while (c && (index < count)) {
    if (c == '\\') {
      escape = true;
    } else {
      if (escape && (c == 'n')) {
        std::cout << std::endl;
      } else {
        std::cout << c;
      }
    }
    c = str[++index];
  }
}
